package nconv.convert;

import nconv.convert.WikiFormatUtils.FmtWiki;
import nconv.convert.WikiFormatUtils.FmtWikiI;
import nconv.convert.WikiMarks.WikiFormatMark;
import nconv.convert.ConvertContext.DOC_TYPE;
import nconv.convert.plugins.DefaultTGPPConvertPlugin;
import nconv.convert.plugins.TGPPConvertPlugin;
import nconv.utils.ClassUtil;
import nconv.utils.LogCenter;
import nconv.utils.Pair;
import org.apache.poi.hwpf.HWPFDocumentCore;
import org.apache.poi.hwpf.usermodel.Paragraph;
import org.apache.poi.hwpf.usermodel.Table;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.Text;

import java.lang.reflect.Modifier;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static nconv.convert.WikiFormatUtils.fmtTemplate;

public class TGPP2WikiConverter extends WikiConverter{

	public static final String ATTR_SPEC_TITLE = "SpecTitle";

	static Map<String, TGPPConvertPlugin> pluginMap = new HashMap<>();

	static {
		Set<Class<?>> classes = ClassUtil.getClasses(TGPPConvertPlugin.class.getPackage());
		for (Class cls : classes) {
			int modifiers = cls.getModifiers();
			if ((modifiers & Modifier.PUBLIC) != 0 &&
					(modifiers & Modifier.ABSTRACT) == 0 &&
					!cls.isInterface()) {
				if (DefaultTGPPConvertPlugin.class.isAssignableFrom(cls)) {
					String name = cls.getSimpleName();
					if (name.charAt(0) == 'P') {
						String specName = name.substring(1);
						try {
							pluginMap.put(specName, (TGPPConvertPlugin)cls.newInstance());
						} catch (Exception e) {
							LogCenter.log("Failed to create instance for class: " + cls.getName());
						}
					}
				}
			}
		}
	}

	private static final TGPPConvertPlugin defaultConvertPlugin = new DefaultTGPPConvertPlugin();
	private static TGPPConvertPlugin activePlugin;
	
	public static void main(String[] args1) throws Exception {
		TGPPRefProcessor.reloadRefs(false);

		//String[] specArray = new String[] {"23203", "29212", "29213", "29214", "29215", "29217", "29219", "29329"};
		//String[] specArray = new String[] {"29212", "29214", "29215", "29217", "29219", "29329"};
		//String[] specArray = new String[] {"29214"};
		String[] specArray = DocUtils.SPEC_NAME_ARRAY;
		ConvertContext.setDocType(DOC_TYPE.TGPP);

		DocUtils.setSpecFileNames(specArray);
		Map<String, String> specFileMap = DocUtils.getSpecDocFileMap();
		for (String spec : specArray) {
			String specFileName = specFileMap.get(spec);
			if (specFileName == null) {
				System.err.println("Failed to locate a word file for spec: " + spec);
				continue;
			}
			activePlugin = pluginMap.get(spec);
			if (activePlugin == null) {
				activePlugin = defaultConvertPlugin;
			}
			ConvertContext.setDocName(specFileName);
			ConvertUtils.processTGPPDoc(specFileName);
		}

		LogCenter.updateLogs();
	}


	
	public TGPP2WikiConverter(String docName) {
		super(docName);
	}

	private final static Pattern annextToCPattern = Pattern.compile("[Aa]nnex\\s+([A-Z])\\s+.*");

	private final static String clauseKeyword = "([cC]lause|[sS]ubclause|[aA]nnex)"; // 1 group
	private final static String clauseBody = "((([0-9][0-9A-Za-z]+)|([A-Z0-9]))(\\.[0-9a-fA-Z]+)*)"; // 5 groups
	private final static String specName = "3GPP\\s+T[SR]\\s+(\\d+\\.\\d+)\\s+(\\[\\d+\\])?"; //2 groups

	//clause 4.a2.3
	private final static Pattern clausePattern = Pattern.compile(clauseKeyword + "\\s+" + clauseBody);

	//clause 4.5.1 of 3GPP TS 29.212 [12]
	private final static Pattern clauseOfPattern = Pattern.compile(clauseKeyword + "\\s+" + clauseBody + "\\s+(of|in)\\s+" + specName);

	//3GPP TS 29.212 [12], clause 4.5.1
	private final static Pattern specClausePattern = Pattern.compile(specName + ",?\\s+" + clauseKeyword + "\\s+" + clauseBody);

	private final static String AVPPatternStr = "([0-9A-Z]\\w+(-[0-9A-Z]\\w+)*)";
	public  final static String LooseAVPPatternStr = "(\\w+(-\\w+)*)";
	private final static Pattern AVPRefPattern = Pattern.compile("\\s+(([0-9A-Z]\\w+(-[0-9A-Z]\\w+)+) AVP)");
	private final static Pattern pureAVPPattern = Pattern.compile("^([0-9A-Z]\\w+(-[0-9A-Z]\\w+)+)$"); //usually appears as table cell content

	//in diameterCommand or parent-AVP definition, last ';' indicates comments
	private final static Pattern AVPCodePattern = Pattern.compile("^\\s*(\\d?\\*\\d?)?\\s*[{\\[<]\\s*" + LooseAVPPatternStr + "\\s*[}\\]>]\\s*[;]{0,1}");
	private final static Pattern AVPDefTitlePattern = Pattern.compile("^" + LooseAVPPatternStr + "\\s+AVP(\\s+\\(.*\\))?$");

	private final static Pattern diameterMessagesTitle = Pattern.compile("^\\s*([A-Z][\\w\\*]+)\\s+[Mm]essages\\s*$");
	private final static Pattern diameterCommandTitle = Pattern.compile("^\\s*([A-Z]\\w+(-[0-9A-Z]\\w+)+\\s+\\(([A-Z]{3,})\\)\\s+[Cc]ommand)\\s*$");

	private final static Pattern enumAndValuePattern = Pattern.compile("^([A-Z0-9_\\-]+)\\s+\\(\\d+\\)\\s*:?$");

	private final static Pattern avpOrMsgChildDetailsFormat = Pattern.compile("^(AVP|[Mm]essage)\\s+[Ff]ormat.*");

	//4.1.2-Gx interface
	private final static Pattern clauseRefPattern = Pattern.compile(clauseBody + "-.+");

	private final static Pattern AVPInTablePattern = Pattern.compile("\\|-\n\\|(\\s*colspan=\"\\d\"\\s*\\|)?"
					+ "(<span id='\\w+'>)*" //G2
					+ LooseAVPPatternStr //G3-G4
					+ "(</span>)*" //G5
					+ "\\s*" //fixed the issue that there could be spaces after AVP name
					+ "(\\s+[^\\|\n]+)?\n+" + //G6
					"\\|(\\d+|TBD)[^\\|]*\\n+" + //code G7
					"\\|[^\n]+\\n+" + //clause
					"\\|([^\n]+)\\n+" + //type G8
					"\\|([^\\|]*)\\n+" + //must G9
					"\\|([^\\|]*)\\n+" + //may  G10
					"\\|([^\\|]*)\\n+" + //should not G11
					"\\|([^\\|]*)\\n+" + //must not G12
					"(\\|([^\\|\\-]*)\\n+)?" //may ency G14
	);

	private final static Set<String> escapedAVP = new HashSet(Arrays.asList(
					"Session-Id",
					"Origin-Host",
					"Origin-Realm",
					"Destination-Host",
					"Destination-Realm",
					"Result-Code",
					"Experimental-Result",
					"Supported-Features",
					"Error-Message",
					"Error-Reporting-Host",
					"Failed-AVP",
					"Proxy-Info",
					"Route-Record",
					"Auth-Application-Id",
					"Vendor-Specific-Application-Id",
					"DRMP",
					"Origin-State-Id",
					"Redirect-Host",
					"Redirect-Host-Usage",
					"Redirect-Max-Cache-Time"
	));

	private final FmtClauseRef fmtClauseRef = new FmtClauseRef();
	private final FmtAVPRef fmtAVPRef = new FmtAVPRef();

	String itfName = null;
	String diameterCommand = null;
	String avpName = null;
	StringBuilder avpDefSB = null;
	Map<String, String> avpDefSectionMap = new HashMap<>();
	Map<String, List<String>> avpParentMap = new HashMap<>();
	Map<String, List<String>> avpInMessageMap = new HashMap<>();

	List<String> definedAVPs = new ArrayList<>();
	List<String> definedDiameterCommands = new ArrayList<>();
	private Map<String, Text> pageChildMap = new HashMap<>();
	private String pageName = null;

	boolean AVPChildDetailsMark = false;

	private SearchChildCache searchCache = null;

	private char firstLevelBulletChar = 0;

	public void summary() {
		super.summary();
		if (avpDefSectionMap.size() > 0) {
			LogCenter.logWiki(ConvertContext.getCoreDocName(), LogCenter.DOC_OPERATION.NOTICE, "Potential missing of AVP pages: " + avpDefSectionMap.keySet());
		}

		/*
		itfName = null;
		diameterCommand = null;
		avpName = null;
		avpDefSB = null;
		avpDefSectionMap.clear();
		avpParentMap.clear();
		avpInMessageMap.clear();
		definedAVPs.clear();
		avpInMessageMap.clear();
		pageChildMap.clear();
		pageName = null;
		AVPChildDetailsMark = false;
		searchCache = null;
		firstLevelBulletChar = 0;
		*/
	}
	
	protected void setFormatAttrs(Element element, Paragraph paragraph, String bulletText) {
		super.setFormatAttrs(element, paragraph, bulletText);
		if (element.hasAttribute(WIKI_STYLE_HEADING)) { // is head
			firstLevelBulletChar = 0;
		}
	}

	protected void postProcessParagraph(Element parentElement, Element element, Paragraph p) {
		super.postProcessParagraph(parentElement, element, p);
		
		if (ConvertUtils.isHeading(element)) {
			String text = getElementText(element);
			int tabIdx = text.indexOf('\t');
			if (tabIdx >= 0) {
				element.setAttribute(HEADING_CLAUSE, TGPPRefProcessor.validateClause(text.substring(0, tabIdx).trim()));
				removeTextBefore(element, text.indexOf(text.substring(tabIdx + 1).trim()));
			} else { //annex heading
				Matcher m = annextToCPattern.matcher(text);
				if (m.find()) {
					element.setAttribute(HEADING_CLAUSE, m.group(1));
					//System.out.println(clause + "\t" + title);
				}
			}
			return;
		}
		String styleName = getStyleName(element);
		if (styleName != null && styleName.length() > 1 
				&& styleName.startsWith("B") 
				&& styleName.charAt(1) >= 0 && styleName.charAt(1) <= '9') {
			formatBxList(styleName.charAt(1) - '0', element);
		}
	}

	protected void compactParagraph(Element parentElement, Element element, Paragraph p) {
		String rawText = getElementText(element).trim();
		if (element.hasAttribute(HEADING_CLAUSE)) {
			String clause = element.getAttribute(HEADING_CLAUSE);
			String divId = TGPPRefProcessor.lookForRefLoc(docName, clause);
			String page = null;
			if (divId != null) {
				if ( divId.indexOf(TGPPRefProcessor.PAGE_DIV_CAT_MARK) >= 0) {
					page = divId.substring(0, divId.indexOf(TGPPRefProcessor.PAGE_DIV_CAT_MARK));
				} else {
					page = divId;
					element.setAttribute(MARK_PAGE_CLAUSE, "true");
				}
				element.setAttribute(PAGE_NAME, page);
				String parentPageName = ConvertContext.getParentPageForPageClause(clause, page);
				if (parentPageName != null) {
					Text parent = pageChildMap.get(parentPageName);
					if (parent == null) {
						Text t = textDocumentFacade.createText("\n\n{{CotListMultiCols|expand=yes|Col=1|\n* [[" + page + "]]}}");
						parentElement.insertBefore(t, element);
						pageChildMap.put(parentPageName, t);
					} else {
						String text = getElementText(parent);
						parent.setData(text.substring(0, text.length() - 2) + "\n* [[" + page + "]]}}");
					}
				}
				ConvertContext.setPageInfo(clause, page);
			}
		}

		super.compactParagraph(parentElement, element, p);

		String styleName = element.getAttribute(ATTR_STYLE_NAME);
		if (styleName.equals("ZT")) {
			String text = getFlattenElementText(element).trim();
			if (text.length() > 0) {
				String specTitle = getDocumentHead().getAttribute(ATTR_SPEC_TITLE);
				specTitle += (specTitle.length() > 0 ? "\n" : "") + text;
				getDocumentHead().setAttribute(ATTR_SPEC_TITLE, specTitle);
				ConvertContext.setDocTitle(specTitle);
			}
		} else if (ConvertUtils.isHeading(element)) {
			String clause = element.getAttribute(HEADING_CLAUSE);
			String divId = TGPPRefProcessor.lookForDivId(docName, clause);
			//add div-id if necessary
			if (divId != null) {
				Matcher m2 = clauseRefPattern.matcher(divId);
				if (m2.find()) {
					System.out.println("TODO-" + divId);
				}
			}

			if (avpDefSB != null) {
				avpDefSectionMap.put(avpName, avpDefSB.toString());
			}

			rawText = activePlugin.getReplacedAVPDefOrDiameterMessageHeading(rawText);
			Matcher m = AVPDefTitlePattern.matcher(rawText);
			if (m.find()) {
				avpName = m.group(1);
				LogCenter.log("Find AVP definition section: " + avpName);
				avpDefSB = new StringBuilder();
			} else {
				avpDefSB = null;

				m = diameterMessagesTitle.matcher(rawText);
				if (m.find()) {
					itfName = m.group(1);
				} else {
					if (itfName != null) {
						m = diameterCommandTitle.matcher(rawText);
						if (m.find()) {
							diameterCommand = m.group(3) + " " + itfName;
							LogCenter.log(diameterCommand + " links to: " + ConvertContext.getPageName() + "#" + m.group(0));
							ConvertUtils.createDiameterCommandPage(diameterCommand,
									ConvertContext.getPageName() + "#" + m.group(1));
							definedDiameterCommands.add(diameterCommand);
						} else {
							diameterCommand = null;
						}
					}
				}
			}

		} else {
			if (avpDefSB != null && !p.isInTable()) {
				avpDefSB.append(getElementText(element));
			}
		}
	}

	public boolean shouldKeepSpaces(String text) {
		return activePlugin.canBeCode(heading, text);
	}

	protected void processTable(HWPFDocumentCore wordDocument, Element flow, Table table) {
		super.processTable(wordDocument, flow, table);
		if ( avpDefSB != null ) {
			avpDefSB.append(getElementText(flow.getLastChild()));
		}
	}

	private class FmtClauseRef extends FmtWiki {

		@Override
		public int getPriority() {
			return 3100;
		}

		@Override
		public void handle(StringBuilder start, StringBuilder end, String markContent) {
			String ref = markContent;
			String currSpec = ref.substring(0, ref.indexOf('-'));
			String currClause = ref.substring(ref.indexOf('-') + 1);
			String refLoc = TGPPRefProcessor.lookForRefLoc(currSpec, currClause);
			if (refLoc == null) {
				//System.err.println("!!!!!Ref not found: " + ref + ", whole: " + map.get(ref));
				start.insert(0, "{{InvalidSpecRef|" + ref + "|<nowiki>");
				end.append("</nowiki>}}");
			} else {
				//System.out.println("External Ref found: " + ref + ", loc: " + refLoc);
				StringBuilder sb = new StringBuilder();
				sb.append("''[[ ");
				String page = null;
				int refLocCatIdx = refLoc.indexOf(TGPPRefProcessor.PAGE_DIV_CAT_MARK);
				if (refLocCatIdx >= 0) {
					page = refLoc.substring(0, refLocCatIdx);
					String divName = refLoc.substring(refLocCatIdx + TGPPRefProcessor.PAGE_DIV_CAT_MARK.length());
					if (pageName == null || !page.equals(pageName)) {
						sb.append(page);
					}
					sb.append("#" + divName + "|");
					if (pageName == null || !page.equals(pageName)) {
						sb.append(page + ":");
					}
					sb.append(divName);
				} else { //reference to a single page
					page = refLoc;
					sb.append(page);
				}
				sb.append(" ]]''");
				start.insert(0, sb.toString() + "<!--");
				end.append("-->");
			}
		}

		public boolean accept(FmtWikiI target) {
			return target.getPriority() != getPriority();
		}
	}

	private class FmtAVPRef extends FmtWiki {

		@Override
		public int getPriority() {
			return 3200;
		}

		@Override
		public void handle(StringBuilder start, StringBuilder end, String markContent) {
			start.insert(0, " [[AVP:" + markContent + "|");
			end.append("]] ");
		}

		public boolean accept(FmtWikiI target) {
			return target.getPriority() != getPriority();
		}
	}

	protected void mergeUserWikiFormatMarks(WikiMarks marks, Element element) {
		super.mergeUserWikiFormatMarks(marks, element);

		if (ConvertUtils.isHeading(element)) {
			if (element.hasAttribute(PAGE_NAME)) {
				pageName = element.getAttribute(PAGE_NAME);
			} else {
				pageName = "";
			}
		}
		final String text = getFlattenElementText(element);

		int length = text.length();
		HashMap<String, String> map = new HashMap<>();
		Matcher m = null;
		if (length >= 25) { //annex A of 3GPP TS 29.212
			m = clauseOfPattern.matcher(text);
			while (m.find()) {
				String holder = m.group(8) + "-" + m.group(2);
				//System.out.println(holder);
				map.put(holder, m.group(0));
				marks.addMark(new WikiFormatMark(fmtClauseRef, holder, m.start(0), m.end(0)));
			}
		}

		if (length >= 22) { //3GPP TS 29.212 annex A
			m = specClausePattern.matcher(text);
			while (m.find()) {
				String holder = m.group(1) + "-" + m.group(4);
				//System.out.println(holder);
				map.put(holder, m.group(0));
				marks.addMark(new WikiFormatMark(fmtClauseRef, holder, m.start(0), m.end(0)));
			}
		}

		if (length >= 7) { //Annex A
			m = clausePattern.matcher(text);
			while (m.find()) {
				String holder = docName + "-" + m.group(2);
				//System.out.println(holder);
				map.put(holder, m.group(0));
				marks.addMark(new WikiFormatMark(fmtClauseRef, holder, m.start(0), m.end(0)));
			}
		}

		//continue with pure AVP pattern
		/*
		if (p.isInTable()) {
			m = pureAVPPattern.matcher(text);
			if (m.find()) {
				String avpName = m.group(1);
				if (!escapedAVP.contains(avpName)) {
					return "[[AVP:" + avpName + "|]]";
				} else {
					return text;
				}
			}
		}
		*/

		if (length >= 9) { //xx-xx AVP
			m = AVPRefPattern.matcher(text);
			while (m.find()) {
				marks.addMark(new WikiFormatMark(fmtAVPRef, m.group(2), m.start(2), m.end(2)));
			}
		}

		if (length >= 7) {
			m = enumAndValuePattern.matcher(text);
			if (m.find()) {
				marks.addMark(new WikiFormatMark(fmtTemplate, "EnumValue", m.start(1), m.end(1)));
			}
		}

		if (element.getAttribute(ATTR_STYLE_NAME).equals("PL") || AVPChildDetailsMark) {
			m = AVPCodePattern.matcher(text);
			if (m.find()) {
				marks.addMark(new WikiFormatMark(WikiFormatUtils.fmtListSymbol, "::", 0, 0));
				String avp = m.group(2);
				if (!avp.equals("AVP")) {
					marks.addMark(new WikiFormatMark(fmtAVPRef, avp, m.start(2), m.end(2)));
					if (avpDefSB != null) {
						List<String> list = avpParentMap.get(avp);
						if (list == null) {
							list = new ArrayList<>();
							avpParentMap.put(avp, list);
						}
						if (!list.contains(avpName))
							list.add(avpName);
					} else if (diameterCommand != null) {
						if (!escapedAVP.contains(avp)) {
							List<String> list = avpInMessageMap.get(avp);
							if (list == null) {
								list = new ArrayList<>();
								avpInMessageMap.put(avp, list);
							}
							if (!list.contains(diameterCommand))
								list.add(diameterCommand);
						}
					}

				}
			} else if (text.contains("::=")){
				marks.addMark(new WikiFormatMark(WikiFormatUtils.fmtListSymbol, ":", 0, 0));
				/*
				final String avpFormatStr = "AVP Format:";
				Element prevSib = (Element)element.getPreviousSibling();
				if (getFlattenElementText(prevSib).startsWith(avpFormatStr)) {
					((Text)prevSib.getFirstChild()).setData("'''" + avpFormatStr + "'''");
				}
				*/
			} else {
				AVPChildDetailsMark = false;
			}
		}

		if (activePlugin.enableDetectionOnChildAVPs() && avpOrMsgChildDetailsFormat.matcher(text).find()) {
			AVPChildDetailsMark = true;
		}
	}

	static class AVPInfo {
		String name;
		String code;
		String valueType;
		String must;
		String may;
		String shouldNot;
		String mustNot;
		String mayEncr;
		String specName;

		public AVPInfo(String name) {
			this.name = name;
			this.specName = ConvertContext.getCoreDocName();
		}

		public String toString() {
			return "AVP: " + name + ", code: " + code + ", value type: " + valueType + ", must: " + must + ", may: " + may +
					", should not: " + shouldNot + ", must not: " + mustNot + ", may encr: " + mayEncr + ", spec: " + specName;
		}
	}

	private class SearchChildCache {
		int idx = 0;
		LinkedList<Node> cachedList = new LinkedList<>();
		Element parent;

		public SearchChildCache(Element parent) {
			this.parent = parent;
		}

		public boolean available(Element parent, int offset) {
			return parent == this.parent && offset >= idx;
		}
	}

	protected Pair<Node, Integer> locateChildElementByTextOffset(Element parent, int offset) {

		int idx = 0;
		LinkedList<Node> list = null;

		if (searchCache != null && searchCache.available(parent, offset)) {
			idx = searchCache.idx;
			list = searchCache.cachedList;
		} else {
			list = new LinkedList<>();
			list.add(parent);
		}
		while (list.size() > 0) {
			Node curr = list.removeFirst();

			if (curr instanceof Text) {
				String data = ((Text)curr).getData();
				if (data != null && idx + data.length() > offset) {
					//update cache
					if (searchCache == null || searchCache.parent != parent){
						searchCache = new SearchChildCache(parent);
					}
					searchCache.idx = idx + data.length();
					searchCache.parent = parent;
					searchCache.cachedList = list;

					return new Pair<Node, Integer>(curr, idx);
				} else {
					idx += data.length();
				}
			} else {
				if (curr.getChildNodes().getLength() > 0) {
					list.addAll(0, ConvertUtils.getChildNodeList(curr));
				}

			}
		}

		return null;
	}


	protected void attachFOT(Element fotElement, Element titleElement) {
		String title = getElementText(titleElement).trim();
		super.attachFOT(fotElement, titleElement);
		if (title.startsWith("Table") && (title.endsWith("specific Diameter AVPs") || activePlugin.isDiameterAVPTable(title))) {
			LogCenter.log("Processing diameter specific AVP table: " + title);
			String tableContent = activePlugin.reviseAVPTable(title, getElementText(fotElement));
			Matcher m = AVPInTablePattern.matcher(tableContent);
			while (m.find()) {
				String g4 = m.group(6);
				if (g4 != null && g4.length() > 0) {
					LogCenter.log("oh, there is something!");
				}

				String avpName = m.group(3).trim();
				String revisedAvpName = activePlugin.getAVPNameInTable(avpName);
				Pair<Node, Integer> childInfo = locateChildElementByTextOffset(fotElement, m.start(3));
				if (childInfo != null) {
					String childText = ((Text) childInfo.first).getData();
					childText = childText.substring(0, m.start(3) - childInfo.second)
							+ "[[AVP:" + revisedAvpName + "|" + revisedAvpName + "]]"
							+ childText.substring(m.start(3) - childInfo.second + avpName.length());
					((Text) childInfo.first).setData(childText);
				}
				if (activePlugin.isAVPDefineEscaped(revisedAvpName)) {
					avpDefSectionMap.remove(revisedAvpName);
					avpInMessageMap.remove(revisedAvpName);
					continue;
				}
				AVPInfo info = new AVPInfo(revisedAvpName);
				info.name = revisedAvpName;
				info.code = getAVPAttr(m.group(7));
				info.valueType = getAVPAttr(m.group(8));
				info.must = getAVPAttr(m.group(9));
				info.may = getAVPAttr(m.group(10));
				info.shouldNot = getAVPAttr(m.group(11));
				info.mustNot = getAVPAttr(m.group(12));
				if (m.group(14) != null) {
					info.mayEncr = getAVPAttr(m.group(14));
				} else {
					info.mayEncr = "";
				}

				ConvertUtils.uploadAVPPage(revisedAvpName, avpDefSectionMap.get(revisedAvpName), info, avpParentMap.get(revisedAvpName), avpInMessageMap.get(revisedAvpName));
				avpDefSectionMap.remove(revisedAvpName);
				avpInMessageMap.remove(revisedAvpName);
				definedAVPs.add(revisedAvpName);
			}
		}

	}

	public void processDocument(HWPFDocumentCore wordDocument) {
		super.processDocument(wordDocument);
		if (avpInMessageMap.size() > 0) {
			for (String avp : avpInMessageMap.keySet()) {
				ConvertUtils.updateAVPInMessage(avp, avpInMessageMap.get(avp));
			}
		}


	}

	private String getAVPAttr(String attr) {
		if (attr == null) return "";
		return attr.replaceAll(" ", "");
	}
	
	private void formatBxList(int level, Element element) {
		String text = this.getElementText(element);
		if (text == null || text.length() == 0) return;
		
		if (level == 1) {
			firstLevelBulletChar = text.charAt(0);
			if (firstLevelBulletChar == '-') {
				firstLevelBulletChar = '*';
			} else {
				firstLevelBulletChar = ':';
			}
		} else { //level 2 or above
			if (firstLevelBulletChar == 0) { //not set or missing
				firstLevelBulletChar = ':';
			}
		}
			
		int contentIdx = 0;		
		char currentLevelBulletChar = ':';
		if (text.charAt(0) == '-') {
			currentLevelBulletChar = '*';
			contentIdx ++;
			text = text.substring(1);
		} 
		if (text.length() == 0) {
			element.setAttribute(WIKI_STYLE_LIST, String.valueOf(firstLevelBulletChar));
		}	else {

			char[] chars = new char[level];
			if (firstLevelBulletChar == '*') {
				Arrays.fill(chars, 0, chars.length, '*');
			} else {
				if (currentLevelBulletChar == ':') {
					Arrays.fill(chars, 0, chars.length, ':');
				} else {
					Arrays.fill(chars, 0, chars.length - 1, ':');
					chars[chars.length - 1] = '*';
				}
			}

			element.setAttribute(WIKI_STYLE_LIST, new String(chars));
			int tabIdx = text.indexOf('\t');
			if (tabIdx >=  0) {
				contentIdx = tabIdx + 1;
				if (currentLevelBulletChar == ':') {
					element.setAttribute(BULLET_LABEL, text.substring(0, tabIdx));
				}
			}
		}
		
		if (contentIdx > 0) {
			removeTextBefore(element, contentIdx);
		}
	}
}
